#include "ReaderImp.h"
#include <fstream>
#include <iostream>
#include "../Data.h"
#include "Util.h"

using namespace std;

namespace KVConfig
{
    const char * ReaderImp::s_test_context = "# Hello, this is a comment.\n \n   FIRST_NAME = Chunjun #first name.\n LAST_NAME = Li #last name.\n\n\nGENDER=male \n AGE= 26 #age";

    ReaderImp::ReaderImp()
        : m_cKVSparator('=')
        , m_cCommentSparator('#')
    {

    }

    void ReaderImp::setKVSparator(char cKVSp)
    {
        m_cKVSparator = cKVSp;
    }

    void ReaderImp::setCommentSparator(char cCommentSp)
    {
        m_cCommentSparator = cCommentSp;
    }

    //!Find first "#", and cut string behind it.
    std::string ReaderImp::cutComment(const std::string &str) const
    {
        std::string::size_type pos = str.find_first_of(m_cCommentSparator);
        if (pos == std::string::npos) {
            return str;
        } else {
            return str.substr(0, pos);
        }
    }

    bool ReaderImp::getKeyAndValue(const std::string &str, std::string& key, std::string& value) const
    {
        std::string::size_type pos = str.find_first_of(m_cKVSparator);
        if (pos == std::string::npos)
            return false;

        std::string strLeft  = str.substr(0, pos);
        std::string strRight = str.substr(pos+1);
        key   = string_strip(strLeft);
        value = string_strip(strRight);
        return true;
    }

    bool ReaderImp::parseOneLine(const std::string &str, std::string &key, std::string &value) const
    {
        std::string strWithoutComment = cutComment(str);

        if (is_empty_string(strWithoutComment))
            return false;

        return getKeyAndValue(strWithoutComment, key, value);
    }

    bool ReaderImp::parseString(const std::string &context, Data &data) const
    {
        std::string::size_type currPos = 0;
        std::string::size_type endlPos = 0;
        do {
            endlPos = context.find('\n', currPos);
            std::string currLine = context.substr(currPos, endlPos-currPos);
            std::string key, value;
            if (parseOneLine(currLine, key, value)) {
                data.set(key, value);
            }
            currPos = endlPos + 1;
        } while (endlPos != std::string::npos);
        return true;
    }

    bool ReaderImp::parseFile(const std::string &file_name, Data &data) const
    {
        ifstream ifile;
        ifile.open(file_name.c_str());
        if (!ifile) {
            cout << "error: open file " << file_name << " fail." << endl;
            return false;
        }

        std::string line;
        while (getline(ifile, line)) {
            std::string key, value;
            if (parseOneLine(line, key, value)) {
                data.set(key, value);
            }
        }
        ifile.close();
        return true;
    }

    /**
     * ====== Test ======
     */

    int ReaderImp::test_cutComment()
    {
        cout << "test_cutComment: " << endl;
        ReaderImp reader;
        std::string str_src = "aa=123 #comment #";
        std::string str_result = reader.cutComment(str_src);
        if (str_result != "aa=123 ") {
            cout << "Fail: " << str_result << endl;
            return -1;
        } else {
            cout << "Pass" << endl;
            return 0;
        }
    }

    int ReaderImp::test_parseOneLine()
    {
        cout << "test_parseOneLine: " << endl;

        int ret = 0;

        ReaderImp reader;

        std::string key, value;
        reader.parseOneLine(" Name = lichunjun # Li", key, value);
        if (key == "Name" && value == "lichunjun") {
            cout << "Pass 1" << endl;
        } else {
            cout << "Fail 1: key:" << key << ", value:" << value << endl;
            ret = -1;
        }

        if (!reader.parseOneLine("    #", key, value)) {
            cout << "Pass 2" << endl;
        } else {
            cout << "Fail 2" << endl;
            ret = -1;
        }

        if (!reader.parseOneLine(" # This is comment ", key, value)) {
            cout << "Pass 3" << endl;
        } else {
            cout << "Fail 3" << endl;
            ret = -1;
        }

        if (!reader.parseOneLine("  aefe 435 ", key, value)) {
            cout << "Pass 4" << endl;
        } else {
            cout << "Fail 4" << endl;
            ret = -1;
        }

        return ret;
    }

    int ReaderImp::test_parseString()
    {
        int ret = 0;
        cout << "test_parseString:" << endl;
        Data data;
        ReaderImp reader;
        if (reader.parseString(s_test_context, data)) {
            cout << "Pass 1" << endl;

            if (data.get("FIRST_NAME") == "Chunjun") {
                cout << "Pass 2" << endl;
            } else {
                cout << "Fail 2: " << data.get("FIRST_NAME") << endl;
                ret = -1;
            }

            if (data.get("LAST_NAME") == "Li") {
                cout << "Pass 3" << endl;
            } else {
                cout << "Fail 3: " << data.get("LAST_NAME") << endl;
                ret = -1;
            }

            if (data.get("GENDER") == "male") {
                cout << "Pass 4" << endl;
            } else {
                cout << "Fail 4: " << data.get("GENDER") << endl;
                ret = -1;
            }

            if (data.get("AGE") == "26") {
                cout << "Pass 5" << endl;
            } else {
                cout << "Fail 5: " << data.get("AGE") << endl;
                ret = -1;
            }

        } else {
            cout << "Fail 1" << endl;
            ret = -1;
        }

        return ret;
    }

    int ReaderImp::test_parseFile()
    {
        int ret = 0;
        cout << "test_parseFile:" << endl;

        const char *file_name = "test.cfg";

        ofstream ofile;
        ofile.open(file_name);
        ofile << s_test_context;
        ofile.close();

        Data data;
        ReaderImp reader;
        if (reader.parseFile(file_name, data)) {
            cout << "Pass 1" << endl;

            if (data.get("FIRST_NAME") == "Chunjun") {
                cout << "Pass 2" << endl;
            } else {
                cout << "Fail 2: " << data.get("FIRST_NAME") << endl;
                ret = -1;
            }

            if (data.get("LAST_NAME") == "Li") {
                cout << "Pass 3" << endl;
            } else {
                cout << "Fail 3: " << data.get("LAST_NAME") << endl;
                ret = -1;
            }

            if (data.get("GENDER") == "male") {
                cout << "Pass 4" << endl;
            } else {
                cout << "Fail 4: " << data.get("GENDER") << endl;
                ret = -1;
            }

            if (data.get("AGE") == "26") {
                cout << "Pass 5" << endl;
            } else {
                cout << "Fail 5: " << data.get("AGE") << endl;
                ret = -1;
            }

        } else {
            cout << "Fail 1" << endl;
            ret = -1;
        }

        return ret;
    }

    int ReaderImp::selfTest()
    {
        if (test_cutComment() == 0
            && util_test() == 0) {
            if (test_parseOneLine() == 0) {
                test_parseString();
                test_parseFile();
            }
        }
        return 0;
    }
}
